package com.douban.media
{
	import com.douban.event.PlayCompleteEvent;
	import com.douban.event.PlayErrorEvent;
	import com.douban.event.PlayProgressEvent;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.utils.Timer;
	import flash.utils.setTimeout;
	
	import net.sevear.chaos.utils.Logger;
	
	public class AudioPlayer extends EventDispatcher
	{
		private var soundLength:Number;
		private var soundUrl:String;
		private var sound:Sound;
		private var channel:SoundChannel;
		private var infoLength:int;
		private var position:int;
		private var timer:Timer;
		private var vol:Number;
		private var percentBuffered:Number;
		private var idleTime:int;
		private var holdTime:int;
		private var tryTimes:int;
		private var recentPlayed:Number;
		private var _status:int;
		private var logger:Logger=Logger.getInstance();
		
		public static const PLAY:int = 0;
		public static const PAUSE:int = 1;
		public static const STOP:int = 2;
		
		public static const TYPE_LOCAL:int 	= 0;
		public static const TYPE_DOUBAN:int = 1;
		public static const TYPE_XIAMI:int 	= 2;
		
		public function AudioPlayer(target:IEventDispatcher=null):void
		{
			super(target);
			this.infoLength = 0;
			this.position = 0;
			this._status = STOP;
			this.vol = 1;
			
			this.sound = new Sound();
			this.sound.addEventListener(IOErrorEvent.IO_ERROR,sound_ioError);
			this.sound.addEventListener(Event.COMPLETE,onSoundLoadComplete);
			this.sound.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS,onHttpResponseStatus);
			this.sound.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
			this.timer = new Timer(40);
			this.timer.addEventListener(TimerEvent.TIMER, onTimer);
			this.loadSound(this.url,TYPE_DOUBAN);
		}
		
		public function set url(value:String):void
		{
			this.loadSound(value,TYPE_DOUBAN);
			return;
		}
		
		public function get url():String
		{
			return this.soundUrl;
		}
		
		public function set volume(value:Number):void
		{
			this.vol = value;
			if (this.channel)
			{
				this.channel.soundTransform = new SoundTransform(value);
			}
			return;
		}
		
		public function get volume():Number
		{
			return this.vol;
		}
		
		private var _curType:int=TYPE_DOUBAN;
		public function play(url:String = null,type:int = TYPE_DOUBAN,len:int = 0):void{
			var url:String = url;
			var len:int = len;
			if(url && url!=this.soundUrl){
				_curType = type;
				this.loadSound(url,_curType);
				this.infoLength = len;
			}
			if(_status!=PAUSE){
				this.position = 0;
			}
			
			if (this.channel)
			{
				this.channel.removeEventListener(Event.SOUND_COMPLETE,onComplete);
			}
			try
			{
				this.channel = this.sound.play(this.position);
			}
			catch(e:Error){
				logger.logError(e.message);
			}
			
			this.channel.addEventListener(Event.SOUND_COMPLETE, this.onComplete);
			this.channel.soundTransform = new SoundTransform(this.vol);
			
			this._status = PLAY;
			this.timer.start();
		}
		
		public function stop():void
		{
			if (this.channel)
			{
				this.channel.stop();
			}
			this._status = STOP;
			this.timer.stop();
			return;
		}
		
		public function pause():void
		{
			this.channel.stop();
			this._status = PAUSE;
			this.timer.stop();
			return;
		}
		
		public function get status():int
		{
			return this._status;
		}
		
		private function seek(param1:Number):void
		{
			if (this._status == PLAY && this.channel)
			{
				this.channel.stop();
				this.channel = this.sound.play(this.soundLength * param1);
			}
			else
			{
				this.channel = this.sound.play(this.soundLength * param1);
				this.position = this.channel.position;
//				trace("seek:this.position = this.channel.position;",this.position);
				this.channel.stop();
			}
			this.channel.soundTransform = new SoundTransform(this.vol);
			return;
		}
		
		private function onTimer(event:TimerEvent):void{
			var loaded:uint;
			var total:int;
			var percentPlayed:Number;
			try
			{
				loaded = this.sound.bytesLoaded;
				total = this.sound.bytesTotal;
				this.soundLength = this.sound.length;
				this.position = this.channel.position;
//				trace("onTimer:this.position = this.channel.position;",this.position);
				this.percentBuffered = loaded / total;
				this.soundLength = this.soundLength / this.percentBuffered;
				percentPlayed = this.position / this.soundLength;
			}
			catch (e:Error)
			{
				logger.logError(e.message);
				return;
			}
			if (this.idleTime > 30 * 2)
			{
				this.idleTime = 0;
				this.onComplete();
			}
			else if (this.holdTime > 30 * 13)
			{
				this.holdTime =0;
				logger.logError("not_play pos:" + loaded + "/" + total + " percent played:" + percentPlayed + " " + this.soundUrl, "ra000");
				this.onError(PlayErrorEvent.ERROR_LOAD_TIMEOUT);
			}
			
			if (this.sound.isBuffering && this.position > 0)
			{
				this.onError(PlayErrorEvent.ERROR_BUFFERING);
			}
			
			if (this.sound.isBuffering && this.position == 0)
			{
				this.onProgress(0);
			}
			else if (this._status == PLAY)
			{
				this.onProgress(this.position);
			}
			if (this._status == PLAY && (percentPlayed === this.recentPlayed || isNaN(percentPlayed)))
			{
				this.holdTime = this.holdTime + 1;
			}
			else
			{
				this.holdTime = 0;
			}
			if (percentPlayed === this.recentPlayed && percentPlayed > 0.5 && !this.sound.isBuffering && this._status == PLAY && this.position > 0)
			{
				if (this.infoLength && this.position / 1000 >= this.infoLength || percentPlayed >= 1)
				{
					this.onComplete();
				}
				this.idleTime+=1;
			}
			else
			{
				this.idleTime = 0;
			}
			if (percentPlayed > this.recentPlayed)
			{
				this.tryTimes = 0;
			}
			this.recentPlayed = percentPlayed;
		}
		
		private function loadSound(url:String,type:int = 0):void{
			if(url!=null){
				if (this.channel)
				{
					this.channel.stop();
				}
				
				try{
					this.sound.close();
					this.sound.removeEventListener(IOErrorEvent.IO_ERROR, this.sound_ioError);
				}
				catch(e:Error){
					
				}
				
				this.soundUrl = url;
				try
				{
					this.sound = new Sound();
					this.sound.addEventListener(IOErrorEvent.IO_ERROR, sound_ioError);
					this.sound.addEventListener(Event.COMPLETE,onSoundLoadComplete);
					this.sound.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS,onHttpResponseStatus);
					this.sound.addEventListener(HTTPStatusEvent.HTTP_STATUS,onHttpResponseStatus);
					this.sound.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
					var rst:URLRequest = new URLRequest();
					rst.url = this.soundUrl;
					
					if(type == TYPE_DOUBAN){
						var head:URLRequestHeader = new URLRequestHeader();
						head.name = "Referer"
						head.value = "http://douban.fm/swf/52000/radioplayer.swf";
						rst.requestHeaders.push(head);
					}
					
					this.sound.load(rst);
				}
				catch (e:Error)
				{
					logger.logError(e.message);
					sound_ioError();
				}
			}
		}
		
		private function sound_ioError(e:IOErrorEvent=null):void{
			var f:*;
			var e:* = e;
//			this.lg.report("sound_ioerror " + this.soundUrl + " " + (this.tryTimes + 1) + " times. " + e.text, "ra009");
//			this.lg.log("load sound io error:" + e.toString() + " retry in 2 secs");
			if (this.tryTimes >= 2)
			{
				this.tryTimes = 0;
				this.onError(PlayErrorEvent.ERROR_IOERROR);
			}
			else
			{
				this.tryTimes +=1;
//				f:Function = function ()
//				{
//					sound.close();
//					sound.load(new URLRequest(soundUrl));
//					return;
//				};
				setTimeout(this.loadSound, 2000, this.soundUrl,_curType);
			}
		}
		
		//TODO:
		private function onSoundLoadComplete(e:Event):void{
			
		}
		
		private function onError(e:String):void
		{
			var ev:PlayErrorEvent = new PlayErrorEvent(e);
			dispatchEvent(ev);
		}
		
		private function onComplete(e:Event = null):void
		{
//			args = new activation;
//			var e:Event = args;
			this.timer.stop();
			if (this.channel)
			{
				try
				{
					this.channel.stop();
				}
				catch (e:Error)
				{
					logger.logError(e.message);
				}
			}
			this._status = STOP;
			dispatchEvent(new PlayCompleteEvent());
		}
		
		private function onProgress(pos:int):void
		{
			dispatchEvent(new PlayProgressEvent(pos));
		}
		
		private function onSecurityError(e:SecurityErrorEvent):void{
			logger.logError(e.text);
		}
		
		private function onHttpResponseStatus(e:HTTPStatusEvent):void{
			logger.logError(e.status);
		}
	}
}